package com.libleveldb.implementation

import com.libleveldb.Iterator
import com.libleveldb.exception.LevelDBClosedException
import com.libleveldb.exception.LevelDBIteratorNotValidException

/**
 * An iterator is used to iterator over the entries in the database according to the total sort order imposed by the
 * comparator.
 */
class NativeIterator(nit: Long) : Iterator() {
    // Don't touch this or all hell breaks loose.
    private var nit: Long

    /**
     * Whether this pointer is valid. An iterator is valid iff it is positioned over a key-value pair.
     *
     * @return whether the iterator is valid
     * @throws com.libleveldb.exception.LevelDBClosedException
     */
    @get:Throws(LevelDBClosedException::class)
    override val isValid: Boolean
        get() {
            checkIfClosed()
            return nativeValid(nit)
        }

    /**
     * Seeks to the first key-value pair in the database.
     *
     * @throws com.libleveldb.exception.LevelDBClosedException
     */
    @Throws(LevelDBClosedException::class)
    override fun seekToFirst() {
        checkIfClosed()
        nativeSeekToFirst(nit)
    }

    /**
     * Seeks to the last key-value pair in the database.
     *
     * NB: Reverse iteration is somewhat slower than forward iteration.
     *
     * @throws com.libleveldb.exception.LevelDBClosedException
     */
    @Throws(LevelDBClosedException::class)
    override fun seekToLast() {
        checkIfClosed()
        nativeSeekToLast(nit)
    }

    /**
     * Seek to the given key, or right after it.
     *
     * @param key the key, never <tt>null</tt>
     * @throws com.libleveldb.exception.LevelDBClosedException
     */
    @Throws(LevelDBClosedException::class)
    override fun seek(key: ByteArray?) {
        checkIfClosed()
        requireNotNull(key) { "Seek key must never be null!" }
        nativeSeek(nit, key)
    }

    /**
     * Advance the iterator forward.
     *
     * Requires: [.isValid]
     *
     * @throws com.libleveldb.exception.LevelDBClosedException
     */
    @Throws(LevelDBIteratorNotValidException::class, LevelDBClosedException::class)
    override fun next() {
        checkIfClosed()
        if (!isValid) {
            throw LevelDBIteratorNotValidException()
        }
        nativeNext(nit)
    }

    /**
     * Advance the iterator backward.
     *
     * Requires: [.isValid]
     *
     * @throws com.libleveldb.exception.LevelDBClosedException
     */
    @Throws(LevelDBIteratorNotValidException::class, LevelDBClosedException::class)
    override fun previous() {
        checkIfClosed()
        if (!isValid) {
            throw LevelDBIteratorNotValidException()
        }
        nativePrev(nit)
    }

    /**
     * Get the key under the iterator.
     *
     * Requires: [.isValid]
     *
     * @return the key under the iterator, <tt>null</tt> if invalid
     * @throws com.libleveldb.exception.LevelDBClosedException
     */
    @Throws(LevelDBIteratorNotValidException::class, LevelDBClosedException::class)
    override fun key(): ByteArray? {
        checkIfClosed()
        if (!isValid) {
            throw LevelDBIteratorNotValidException()
        }
        return nativeKey(nit)
    }

    /**
     * Get the value under the iterator.
     *
     * Requires: [.isValid]
     *
     * @return the value under the iterator, <tt>null</tt> if invalid
     * @throws com.libleveldb.exception.LevelDBClosedException
     */
    @Throws(LevelDBIteratorNotValidException::class, LevelDBClosedException::class)
    override fun value(): ByteArray? {
        checkIfClosed()
        if (!isValid) {
            throw LevelDBIteratorNotValidException()
        }
        return nativeValue(nit)
    }

    /**
     * Whether this iterator has been closed.
     *
     * @return
     */
    override val isClosed: Boolean
        get() = nit == 0L

    /**
     * Closes this iterator. It will be almost unusable after.
     *
     * Always close the iterator before closing the database.
     */
    override fun close() {
        if (!isClosed) {
            nativeClose(nit)
        }
        nit = 0
    }

    /**
     * Checks if this iterator has been closed.
     *
     * @throws com.libleveldb.exception.LevelDBClosedException
     */
    @Throws(LevelDBClosedException::class)
    private fun checkIfClosed() {
        if (isClosed) {
            throw LevelDBClosedException("Iterator has been closed.")
        }
    }

    companion object {
        private external fun nativeClose(nit: Long)
        private external fun nativeValid(nit: Long): Boolean
        private external fun nativeSeek(nit: Long, key: ByteArray)
        private external fun nativeSeekToFirst(nit: Long)
        private external fun nativeSeekToLast(nit: Long)
        private external fun nativeNext(nit: Long)
        private external fun nativePrev(nit: Long)
        private external fun nativeKey(nit: Long): ByteArray?
        private external fun nativeValue(nit: Long): ByteArray?
    }

    /**
     * Protected constructor used in [NativeLevelDB.iterator].
     *
     * @param nit the nat pointer
     */
    init {
        require(nit != 0L) { "Native iterator pointer must not be NULL!" }
        this.nit = nit
    }
}